﻿//------------------------------------------------------------------------------
// Copyright © FRA & FV 2014
// All rights reserved
//------------------------------------------------------------------------------
// Binary Serialization SDK
//
// SVN revision information:
//   $Revision: 90 $
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.Text;

namespace FRAFV.Binary.Serialization
{
	/// <summary>Writes primitive types in binary to a stream with specific byte order and supports writing strings in a specific encoding.</summary>
	public class BinWriter : BinaryWriter
	{
		private byte[] buffer = new byte[8];

		/// <summary>Indicates the byte order in which data is written to a stream.</summary>
		/// <returns>The byte order for a stream.</returns>
		public bool IsLittleEndian { get; set; }

		/// <summary>Initializes a new instance of the <see cref="T:FRAFV.Binary.Serialization.BinWriter" /> class
		///   based on the supplied stream and using <see cref="T:System.Text.UTF8Encoding" />.</summary>
		/// <param name="output">The output stream.</param>
		/// <exception cref="T:System.ArgumentException">The stream does not support writing, or the stream is already closed. </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="output" /> is null. </exception>
		public BinWriter(Stream output)
			: base(output)
		{
			this.IsLittleEndian = BitConverter.IsLittleEndian;
		}

		/// <summary>Initializes a new instance of the <see cref="T:FRAFV.Binary.Serialization.BinWriter" /> class
		///   based on the supplied stream and a specific character encoding.</summary>
		/// <param name="output">The output stream.</param>
		/// <param name="encoding">The character encoding. </param>
		/// <exception cref="T:System.ArgumentException">The stream does not support writing, or the stream is already closed. </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="output" /> or <paramref name="encoding" /> is null. </exception>
		public BinWriter(Stream output, Encoding encoding)
			: base(output, encoding)
		{
			this.IsLittleEndian = BitConverter.IsLittleEndian;
		}

		/// <summary>Initializes a new instance of the <see cref="T:FRAFV.Binary.Serialization.BinWriter" /> class
		///   based on the supplied stream with specific byte order and using <see cref="T:System.Text.UTF8Encoding" />.</summary>
		/// <param name="output">The output stream.</param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">The stream does not support writing, or the stream is already closed. </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="output" /> is null. </exception>
		public BinWriter(Stream output, bool littleEndian)
			: base(output)
		{
			this.IsLittleEndian = littleEndian;
		}

		/// <summary>Initializes a new instance of the <see cref="T:FRAFV.Binary.Serialization.BinWriter" /> class
		///   based on the supplied stream with specific byte order and a specific character encoding.</summary>
		/// <param name="output">The output stream.</param>
		/// <param name="encoding">The character encoding. </param>
		/// <param name="littleEndian">true to use the little-endian byte order (most significant byte first), or false
		///   to use the big-endian byte order (least significant byte first).</param>
		/// <exception cref="T:System.ArgumentException">The stream does not support writing, or the stream is already closed. </exception>
		/// <exception cref="T:System.ArgumentNullException">
		///   <paramref name="output" /> or <paramref name="encoding" /> is null. </exception>
		public BinWriter(Stream output, Encoding encoding, bool littleEndian)
			: base(output, encoding)
		{
			this.IsLittleEndian = littleEndian;
		}

		/// <summary>Writes a 2-byte signed integer to the current stream and advances the stream position by two bytes.</summary>
		/// <param name="value">The 2-byte signed integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(short value)
		{
			BinConverter.InternalFromInt16(value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 2);
		}

		/// <summary>Writes a 3-byte integer to the current stream and advances the stream position by three bytes.</summary>
		/// <param name="value">The 3-byte integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public virtual void WriteInt24(int value)
		{
			BinConverter.InternalFromInt24(value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 3);
		}

		/// <summary>Writes a 4-byte signed integer to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="value">The 4-byte signed integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(int value)
		{
			BinConverter.InternalFromInt32(value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 4);
		}

		/// <summary>Writes an 8-byte signed integer to the current stream and advances the stream position by eight bytes.</summary>
		/// <param name="value">The 8-byte signed integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(long value)
		{
			BinConverter.InternalFromInt64(value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 8);
		}

		/// <summary>Writes a 2-byte unsigned integer to the current stream and advances the stream position by two bytes.</summary>
		/// <param name="value">The 2-byte unsigned integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(ushort value)
		{
			BinConverter.InternalFromInt16((short)value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 2);
		}

		/// <summary>Writes a 4-byte unsigned integer to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="value">The 4-byte unsigned integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(uint value)
		{
			BinConverter.InternalFromInt32((int)value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 4);
		}

		/// <summary>Writes an 8-byte unsigned integer to the current stream and advances the stream position by eight bytes.</summary>
		/// <param name="value">The 8-byte unsigned integer to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(ulong value)
		{
			BinConverter.InternalFromInt64((long)value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 8);
		}

		/// <summary>Writes a 4-byte floating-point value to the current stream and advances the stream position by four bytes.</summary>
		/// <param name="value">The 4-byte floating-point value to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(float value)
		{
			BinConverter.InternalFromSingle(value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 4);
		}

		/// <summary>Writes an 8-byte floating-point value to the current stream and advances the stream position by eight bytes.</summary>
		/// <param name="value">The 8-byte floating-point value to write. </param>
		/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
		/// <exception cref="T:System.ObjectDisposedException">The stream is closed. </exception>
		public override void Write(double value)
		{
			BinConverter.InternalFromDouble(value, IsLittleEndian, this.buffer);
			this.OutStream.Write(this.buffer, 0, 8);
		}
	}
}
